package com.gilight.base.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gilight.base.admin.brand.AdminBrandService;
import com.gilight.base.base.BusinessException;
import com.gilight.base.base.ParameterException;
import com.gilight.base.base.RepStatusCode;
import com.gilight.base.base.exception.AppServiceException;
import com.gilight.base.base.exception.ExceptionDefinition;
import com.gilight.base.base.exception.ServiceException;
import com.gilight.base.base.exception.ThirdPartServiceException;
import com.gilight.base.common.Const;
import com.gilight.base.component.CacheComponent;
import com.gilight.base.component.CacheConst;
import com.gilight.base.dao.SysRoleMapper;
import com.gilight.base.dao.SysUserAccessLogMapper;
import com.gilight.base.dao.SysUserMapper;
import com.gilight.base.dao.SysUserZzjgMapper;
import com.gilight.base.dto.CouponDTO;
import com.gilight.base.dto.UserDto;
import com.gilight.base.dto.uni.UserDTO;
import com.gilight.base.enums.UserLoginType;
import com.gilight.base.mapper.UserMapper;
import com.gilight.base.model.*;
import com.gilight.base.model.uni.UserDO;
import com.gilight.base.notify.SMSClient;
import com.gilight.base.notify.SMSResult;
import com.gilight.base.properties.UnimallWxAppProperties;
import com.gilight.base.service.UserService;
import com.gilight.base.uiapp.coupon.CouponService;
import com.gilight.base.util.*;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.apache.commons.codec.digest.Md5Crypt;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    SysUserAccessLogMapper sysUserAccessLogMapper;
    @Resource
    private SysUserZzjgMapper sysUserZzjgMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    /**
     * //TODO 小程序添加
     */
    //以下为小程序新增加的实现



    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private SMSClient smsClient;

    @Autowired
    private CacheComponent cacheComponent;

    @Autowired
    private UserBizService userBizService;

    @Autowired
    private StringRedisTemplate userRedisTemplate;

    private OkHttpClient okHttpClient = new OkHttpClient();

    @Autowired
    private UnimallWxAppProperties unimallWxProperties;

    @Autowired
    private AdminBrandService adminBrandService;

    @Autowired
    private CouponService couponService;

    @Override
    public void login(SysUser sysUser) {
        Subject subject = SecurityUtils.getSubject();
        SimpleHash password = new SimpleHash("MD5", sysUser.getPassword());
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(sysUser.getUserCode(), password.toHex());
        try {
            subject.login(usernamePasswordToken);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("用户名或密码不正确");
        }
        SysUserAccessLog sysUserAccessLog = new SysUserAccessLog();
        SysUser sysUser1 = ShiroUtil.getUser();
        SysUser sysUser2 = sysUserMapper.selectByPrimaryKey(sysUser1.getId());
        sysUserAccessLog.setUserCode(sysUser2.getUserCode());
        sysUserAccessLog.setUserName(sysUser2.getUserName());
        sysUserAccessLog.setType(1);
//        Session session = SecurityUtils.getSubject().getSession();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        sysUserAccessLog.setIp(IPUtils.getIpAddr(request));
        sysUserAccessLogMapper.insertSelective(sysUserAccessLog);
    }

    @Override
    public PageInfo findList(UserDto userDto) {
        Map<String, Object> map = new HashMap<>();
        if (StringUtils.isNotEmpty(userDto.getUserName())) {
            map.put("userName", "%" + userDto.getUserName() + "%");
        }
        map.put("roleId", userDto.getRoleId());
        map.put("userCode", StringTool.wrapWithPercentChar(userDto.getUserCode()));
        map.put("status", userDto.getStatus());
        map.put("id",userDto.getId());
        PageHelper.startPage(userDto.getPageNum(), userDto.getPageSize());
        List<SysUserDTO> list = sysUserMapper.findList(map);
        for (SysUserDTO sysUser : list) {
            List<SysRole> roleList = sysRoleMapper.selectByUserId(sysUser.getId());
            sysUser.setRoleList(roleList);
        }
        return new PageInfo<>(list);
    }

    public SysUser selectByUserCode(String userCode) {
        return sysUserMapper.selectByUserCode(userCode);
    }

    @Override
    @Transactional
    public void addUser(SysUser sysUser) {
        if (StringUtils.isEmpty(sysUser.getUserCode())) {
            throw new BusinessException("账号不能为空");
        }
        SysUser old = this.selectByUserCode(sysUser.getUserCode());
        Long userId;
        if (old != null) {
            userId = old.getId();
        } else {
            //用户不存在，则新增用户
            if (StringUtils.isEmpty(sysUser.getPassword())) {
                throw new BusinessException("密码不能为空");
            }
            String salt = "";
            String password = sysUser.getPassword();
            String encodedPassword = new SimpleHash("MD5", password, salt).toString();
            sysUser.setSalt(salt);
            sysUser.setPassword(encodedPassword);
            sysUserMapper.insertSelective(sysUser);
            userId = sysUser.getId();
        }
        //绑定部门
        this.addUserZzjg(userId+"", sysUser.getZzjgId());
        //分配角色
        this.assign(userId, sysUser.getRoleIdList());
    }

    //给用户分配角色
    @Transactional
    public void assign(Long userId, List<Long> roleIdList) {
        sysUserMapper.delUserRoleByUserId(userId);
        sysUserMapper.insertBatchUserRole(userId, roleIdList);
    }

    //增加用户组织机构
    private void addUserZzjg(String userId, String zzjgId) {
        Map<String, Object> userZzjg = new HashMap<>();
        userZzjg.put("zzjgId", zzjgId);
        userZzjg.put("userId", userId);
        List<Map<String, Object>> list = sysUserZzjgMapper.getByUserZzjg(userZzjg);
        if (CollectionUtils.isNotEmpty(list)) {
            throw new BusinessException("该部门下已有该用户");
        } else {
            SysUserZzjg sysUserZzjg = new SysUserZzjg();
            sysUserZzjg.setZzjgId(zzjgId);
            sysUserZzjg.setUserId(userId);
            sysUserZzjgMapper.insertSelective(sysUserZzjg);
        }
    }

    @Override
    public void edit(SysUser sysUser) {
        SysUser updateUser = new SysUser();
        updateUser.setId(sysUser.getId());
        updateUser.setUserName(sysUser.getUserName());
        sysUserMapper.updateByPrimaryKeySelective(sysUser);
        //分配角色
        this.assign(sysUser.getId(), sysUser.getRoleIdList());
    }

    @Override
    public void resetPassword(Long id) {
        SysUser sysUser = new SysUser();
        sysUser.setId(id);
        String salt = "";
        String password = "123456789";
        String encodedPassword = new SimpleHash("MD5", password, salt).toString();
        sysUser.setSalt(salt);
        sysUser.setPassword(encodedPassword);
        sysUserMapper.updateByPrimaryKeySelective(sysUser);
    }

    @Override
    public void updatePassword(SysUser sysUser) {
        if (sysUser.getId()==null) {
            throw new BusinessException("id不能为空");
        }
        if (StringUtils.isEmpty(sysUser.getPassword())) {
            throw new BusinessException("密码不能为空");
        }
        SysUser updateUser = new SysUser();
        updateUser.setId(sysUser.getId());
        updateUser.setPassword(sysUser.getPassword());
        String salt = "";
        String encodedPassword = new SimpleHash("MD5", sysUser.getPassword(), salt).toString();
        updateUser.setPassword(encodedPassword);
        sysUserMapper.updateByPrimaryKeySelective(updateUser);
    }

    @Override
    public void delete(Map<String, Object> map) {
        List<Map<String, Object>> userList = (List<Map<String, Object>>) map.get("userList");
        if (CollectionUtils.isEmpty(userList)) {
            throw new ParameterException("请先选择要删除的用户!");
        }
        for (Map<String, Object> userMap : userList) {
            String userIdStr = MapUtils.getString(userMap, "userId");
            String zzjgId = MapUtils.getString(userMap, "zzjgId");
            Long userId = Long.parseLong(userIdStr);
            if (userId==1L) {
                throw new BusinessException("该用户不可删除");
            }
            //删除该组织机构下用户
            sysUserZzjgMapper.delUserZzjg(zzjgId, userIdStr);
            //如果所有机构中都已删除该用户 从登录表中删除该用户
            List list = sysUserZzjgMapper.getUserZzjgsByUserId(userId);
            if (CollectionUtils.isEmpty(list)) {
                sysUserMapper.deleteByPrimaryKey(userId);
            }
        }

    }

    @Override
    public SysUser getUserById(Long id) {
        return sysUserMapper.selectByPrimaryKey(id);
    }

    @Override
    public void updateStatus(Long id, Integer status) {
        SysUser user = new SysUser();
        user.setId(id);
        user.setStatus(status);
        sysUserMapper.updateByPrimaryKeySelective(user);
    }

    public void selectStudentByExamineeNumber(String examineeNumber){
        Map<String,Object> stuNum = sysUserMapper.selectStudentByExamineeNumber(examineeNumber);
        if(stuNum == null){
            throw new BusinessException("考生不存在，不允许登录");
        }
        String num = stuNum.get("num") != null ? String.valueOf(stuNum.get("num")) : "";
        if(StringUtils.isBlank(num))throw new BusinessException("考生不存在，不允许登录");
        if(Long.valueOf(num).intValue() <= 0 )throw new BusinessException("考生不存在，不允许登录");
    }

    @Override
    public Map getCurrentUserAndDept() {
        SysUser sysUser = ShiroUtil.getUser();
        if (null == sysUser){
            throw new ParameterException("您还没有登录");
        }
        Map map = new HashMap();
        Long userId = sysUser.getId();
        map.put("userId",userId);
        map.put("userCode",sysUser.getUserCode());
        map.put("userName",sysUser.getUserName());
        List<Map<String, Object>> deptList = new ArrayList<>();
        map.put("deptList",deptList);

        if (null != userId){
            // 查询组织机构code和名称
            deptList = sysUserZzjgMapper.getUserZzjgsByUserId(userId);
            map.put("deptList",deptList);
        }
        return map;
    }

    @Override
    public Map<String, Object> getProps(String userCode) {
        Map<String, Object> map = sysUserMapper.getPropsAsJzg(userCode);
        if (org.apache.commons.collections4.MapUtils.isEmpty(map)) {
            map = sysUserMapper.getPropsAsStu(userCode);
        }
        return map;
    }

    //***********************小程序添加******************************//

    /**
     * 小程序添加
     * //TODO  ABC
     * @param phone
     * @return
     * @throws ServiceException
     */
    @Override
    public String sendVerifyCode(String phone) throws ServiceException {
        String verifyCode = GeneratorUtil.genSixVerifyCode();
        SMSResult res = smsClient.sendRegisterVerify(phone, verifyCode);
        if (res.isSucc()) {
            cacheComponent.putRaw(CacheConst.USER_VERIFY_CODE_PREFIX + phone, verifyCode, 300);
            return "ok";
        } else {
            throw new AppServiceException(res.getMsg(), ExceptionDefinition.USER_SEND_VERIFY_FAILED.getCode(), RepStatusCode.BUSINESS_ERROR);
        }

    }

    @Override
    @Transactional
    public String register(String phone, String password, String verifyCode, String ip) throws ServiceException {
        //1.校验验证码
        checkVerifyCode(phone, verifyCode);
        //2.校验用户是否存在
        Integer count = userMapper.selectCount(
                new QueryWrapper<UserDO>()
                        .eq("phone", phone));
        if (count > 0) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_HAS_EXISTED);
        }
        //3.校验成功，注册用户
        Date now = new Date();
        UserDO userDO = new UserDO();
        userDO.setPhone(phone);
        userDO.setPassword(Md5Crypt.md5Crypt(password.getBytes(), "$1$" + phone.substring(0, 7)));
        userDO.setLastLoginIp(ip);
        userDO.setGmtLastLogin(now);
        userDO.setGmtUpdate(now);
        userDO.setGmtCreate(now);
        userDO.setLoginType(UserLoginType.REGISTER.getCode());
        userMapper.insert(userDO);
        //返回用户DTO
        cacheComponent.del(CacheConst.USER_VERIFY_CODE_PREFIX + phone);
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String bindPhone(String phone, String password, String verifyCode, Long userId) throws ServiceException {
        //1.校验验证码
        checkVerifyCode(phone, verifyCode);
        //2.校验用户是否存在
        Integer count = userMapper.selectCount(
                new QueryWrapper<UserDO>()
                        .eq("phone", phone));
        if (count > 0) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_HAS_EXISTED);
        }
        //3.校验成功，绑定手机
        UserDO updateUserDO = new UserDO();
        updateUserDO.setId(userId);
        updateUserDO.setPhone(phone);
        updateUserDO.setGmtUpdate(new Date());
        if (userMapper.updateById(updateUserDO) > 0) {
            cacheComponent.del(CacheConst.USER_VERIFY_CODE_PREFIX + phone);
            return "ok";
        }
        throw new AppServiceException(ExceptionDefinition.USER_UNKNOWN_EXCEPTION);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String resetPassword(String phone, String password, String verifyCode) throws ServiceException {
        //1.校验验证码
        checkVerifyCode(phone, verifyCode);
        //2.校验用户是否存在
        List<UserDO> targetUserList = userMapper.selectList(
                new QueryWrapper<UserDO>()
                        .eq("phone", phone));
        if (org.springframework.util.CollectionUtils.isEmpty(targetUserList)) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_NOT_EXIST);
        }
        Long id = targetUserList.get(0).getId();
        //3.校验成功，重置密码
        UserDO updateUserDO = new UserDO();
        updateUserDO.setId(id);
        updateUserDO.setPassword(Md5Crypt.md5Crypt(password.getBytes(), "$1$" + phone.substring(0, 7)));
        updateUserDO.setGmtUpdate(new Date());
        if (userMapper.updateById(updateUserDO) > 0) {
            cacheComponent.del(CacheConst.USER_VERIFY_CODE_PREFIX + phone);
            return "ok";
        }
        throw new AppServiceException(ExceptionDefinition.USER_UNKNOWN_EXCEPTION);
    }

    /**
     * 验证码抽取校验
     *
     * @param phone
     * @param verifyCode
     * @throws ServiceException
     */
    private void checkVerifyCode(String phone, String verifyCode) throws ServiceException {
        String raw = cacheComponent.getRaw(CacheConst.USER_VERIFY_CODE_PREFIX + phone);
        if (org.springframework.util.StringUtils.isEmpty(raw)) {
            throw new AppServiceException(ExceptionDefinition.USER_VERIFY_CODE_NOT_EXIST);
        }
        if (!raw.equals(verifyCode)) {
            throw new AppServiceException(ExceptionDefinition.USER_VERIFY_CODE_NOT_CORRECT);
        }
    }

    @Override
    @Transactional
    public UserDTO login(String phone, String password, Integer loginType, String raw, String ip) throws ServiceException {
        String cryptPassword = Md5Crypt.md5Crypt(password.getBytes(), "$1$" + phone.substring(0, 7));
        UserDTO userDTO = userMapper.login(phone, cryptPassword);
        if (userDTO == null) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_OR_PASSWORD_NOT_CORRECT);
        }
        //检查帐号是否已经冻结
        if (userDTO.getStatus() == 0) {
            throw new AppServiceException(ExceptionDefinition.USER_CAN_NOT_ACTICE);
        }
        if (!org.springframework.util.StringUtils.isEmpty(raw) && UserLoginType.contains(loginType)) {
            if (loginType == UserLoginType.MP_WEIXIN.getCode()) {
                try {
                    JSONObject thirdPartJsonObject = JSONObject.parseObject(raw);
                    String code = thirdPartJsonObject.getString("code");
                    String body = okHttpClient.newCall(new Request.Builder()
                            .url("https://api.weixin.qq.com/sns/jscode2session?appid=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? this.unimallWxProperties.getMiniAppId() : this.unimallWxProperties.getAppId()) +
                                    "&secret=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? this.unimallWxProperties.getMiniAppSecret() : this.unimallWxProperties.getAppSecret()) +
                                    "&grant_type=authorization_code&js_code=" + code).get().build()).execute().body().string();
                    JSONObject jsonObject = JSONObject.parseObject(body);
                    Integer errcode = jsonObject.getInteger("errcode");
                    if (errcode == null || errcode == 0) {
                        String miniOpenId = jsonObject.getString("openid");
                        //将此次登录的openId，暂且放入user的域里面，支付的时候会用到
                        userDTO.setLoginType(loginType);
                        userDTO.setOpenId(miniOpenId);
                    }
                } catch (Exception e) {
                    logger.error("[微信第三方登录] 异常", e);
                    throw new ThirdPartServiceException(ExceptionDefinition.THIRD_PART_SERVICE_EXCEPTION.getMsg(), ExceptionDefinition.THIRD_PART_SERVICE_EXCEPTION.getCode());
                }
            }
        }
        String accessToken = GeneratorUtil.genSessionId();
        //放入SESSION专用Redis数据源中
        userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
        userDTO.setAccessToken(accessToken);
        UserDO userUpdateDO = new UserDO();
        userUpdateDO.setId(userDTO.getId());
        userUpdateDO.setGmtLastLogin(new Date());
        userUpdateDO.setLastLoginIp(ip);
        userMapper.updateById(userUpdateDO);
        return userDTO;
    }

    @Override
    public String logout(String accessToken, Long userId) throws ServiceException {
        userRedisTemplate.delete(accessToken);
        return "ok";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserDTO thirdPartLogin(Integer loginType, Long brandType, String ip, String raw) throws ServiceException {
        try {
            if (UserLoginType.MP_WEIXIN.getCode() == loginType) {
                return wechatSafeLogin(UserLoginType.MP_WEIXIN.getCode(),brandType, ip, raw);
            } else if (UserLoginType.H5_WEIXIN.getCode() == loginType) {
                return wechatH5Login(ip, raw);
            } else if (UserLoginType.APP_WEIXIN.getCode() == loginType) {
                return wechatAppLogin(ip, raw);
            } else {
                throw new AppServiceException(ExceptionDefinition.USER_THIRD_PART_NOT_SUPPORT);
            }
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            logger.error("[用户第三方登录] 异常", e);
            throw new AppServiceException(ExceptionDefinition.USER_THIRD_PART_LOGIN_FAILED);
        }
    }

    @Override
    public String syncUserInfo(String nickname, String avatarUrl, Integer gender, Long birthday, String accessToken, Long userId) throws ServiceException {
        UserDO updateUserDO = new UserDO();
        updateUserDO.setId(userId);
        updateUserDO.setNickname(nickname);
        updateUserDO.setAvatarUrl(avatarUrl);
        updateUserDO.setGender(gender);
        updateUserDO.setGmtUpdate(new Date());
        if (birthday != null)
            updateUserDO.setBirthday(new Date(birthday));
        if (userMapper.updateById(updateUserDO) > 0) {
            //更新SESSION缓存
            UserDTO user = SessionUtil.getUser();
            if (!org.springframework.util.StringUtils.isEmpty(nickname)) {
                user.setNickname(nickname);
            }
            if (!org.springframework.util.StringUtils.isEmpty(avatarUrl)) {
                user.setAvatarUrl(avatarUrl);
            }
            if (birthday != null) {
                user.setBirthday(new Date(birthday));
            }
            if (gender != null) {
                user.setGender(gender);
            }
            userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(user));
            return "ok";
        }
        throw new AppServiceException(ExceptionDefinition.USER_UNKNOWN_EXCEPTION);
    }

    @Override
    public Object getH5Sign(String url) throws ServiceException {
        try {
            String wxH5AccessToken = userBizService.getWxH5AccessToken();
            //我也不知道为什么微信这里要换两次
            String wxH5Ticket = userBizService.getWxH5Ticket(wxH5AccessToken);
            String noncestr = GeneratorUtil.genUUId();
            long timestamp = System.currentTimeMillis();
            StringBuilder sb = new StringBuilder();
            sb.append("jsapi_ticket=");
            sb.append(wxH5Ticket);
            sb.append("&noncestr=");
            sb.append(noncestr);
            sb.append("&timestamp=");
            sb.append(timestamp);
            sb.append("&url=");
            sb.append(url);
            //明文
            String content = sb.toString();
            String signature = SHAUtil.sha256Encode(content);
            Map<String, Object> obj = new HashMap<>();
            obj.put("noncestr", noncestr);
            obj.put("timestamp", timestamp);
            obj.put("sign", signature);
            return obj;
        } catch (Exception e) {
            logger.info("[获取H5签名] 异常", e);
            throw new AppServiceException(ExceptionDefinition.APP_UNKNOWN_EXCEPTION);
        }
    }

    @Override
    public Boolean checkLogin(String accessToken) throws ServiceException {
        return userRedisTemplate.hasKey(Const.USER_REDIS_PREFIX + accessToken);
    }


    /*************************第三方登录 START******************************/
    private String getBody(Integer loginType, Long brandType, String code){
        String body = "";
        try {
            BrandDo brandDo = adminBrandService.getById(brandType);
            if(brandDo!=null&&StringUtils.isNotEmpty(brandDo.getMiniAppId())&&StringUtils.isNotEmpty(brandDo.getMiniAppSec())){
                body = okHttpClient.newCall(new Request.Builder()
                        .url("https://api.weixin.qq.com/sns/jscode2session?appid=" +
                                (UserLoginType.MP_WEIXIN.getCode() == loginType ? brandDo.getMiniAppId() : this.unimallWxProperties.getAppId())
                                +
                                "&secret=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? brandDo.getMiniAppSec() : this.unimallWxProperties.getAppSecret()) +
                                "&grant_type=authorization_code&js_code=" + code).get().build()).execute().body().string();
            }else{
                body = okHttpClient.newCall(new Request.Builder()
                        .url("https://api.weixin.qq.com/sns/jscode2session?appid=" +
                                (UserLoginType.MP_WEIXIN.getCode() == loginType ? this.unimallWxProperties.getMiniAppId() : this.unimallWxProperties.getAppId())
                                +
                                "&secret=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? this.unimallWxProperties.getMiniAppSecret() : this.unimallWxProperties.getAppSecret()) +
                                "&grant_type=authorization_code&js_code=" + code).get().build()).execute().body().string();
            }
        }catch (Exception e){
            try {
                body = okHttpClient.newCall(new Request.Builder()
                        .url("https://api.weixin.qq.com/sns/jscode2session?appid=" +
                                (UserLoginType.MP_WEIXIN.getCode() == loginType ? this.unimallWxProperties.getMiniAppId() : this.unimallWxProperties.getAppId())
                                +
                                "&secret=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? this.unimallWxProperties.getMiniAppSecret() : this.unimallWxProperties.getAppSecret()) +
                                "&grant_type=authorization_code&js_code=" + code).get().build()).execute().body().string();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }
        return body;
    }
    /**
     * 微信小程序第三方登录，或微信APP安全登录
     * @param loginType MP_WEIXIN(1, "WX小程序登录"), APP_WEIXIN(2, "WX第三方登录")
     * @param brandType
     * @param ip
     * @param raw
     * @return
     * @throws Exception
     */
    private UserDTO wechatSafeLogin(Integer loginType, Long brandType, String ip, String raw) throws Exception {
        //微信第三方登录
        JSONObject thirdPartJsonObject = JSONObject.parseObject(raw);
        String code = thirdPartJsonObject.getString("code");
        String body = this.getBody(loginType,brandType,code);

//        body = okHttpClient.newCall(new Request.Builder()
//                .url("https://api.weixin.qq.com/sns/jscode2session?appid=" +
//                        (UserLoginType.MP_WEIXIN.getCode() == loginType ? this.unimallWxProperties.getMiniAppId() : this.unimallWxProperties.getAppId())
//                        +
//                        "&secret=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? this.unimallWxProperties.getMiniAppSecret() : this.unimallWxProperties.getAppSecret()) +
//                        "&grant_type=authorization_code&js_code=" + code).get().build()).execute().body().string();
        JSONObject jsonObject = JSONObject.parseObject(body);
        Integer errcode = jsonObject.getInteger("errcode");
        if (errcode == null || errcode == 0) {
            String miniOpenId = jsonObject.getString("openid");
            List<UserDO> userDOS = userMapper.selectList(new QueryWrapper<UserDO>().eq("open_id", miniOpenId).eq("login_type", loginType).eq("brand_type",brandType));
            UserDO userDO;
            if (org.springframework.util.CollectionUtils.isEmpty(userDOS)) {
                //若用户为空，则注册此用户
                Date now = new Date();
                UserDO newUserDO = new UserDO();
                newUserDO.setLoginType(loginType);
                newUserDO.setOpenId(miniOpenId);
                newUserDO.setLastLoginIp(ip);
                newUserDO.setGmtLastLogin(now);
                newUserDO.setGmtUpdate(now);
                newUserDO.setGmtCreate(now);
                newUserDO.setBrandType(brandType);
                userMapper.insert(newUserDO);
                //这一步是为了封装上数据库上配置的默认值
                userDO = userMapper.selectById(newUserDO.getId());
                //TODO 新注册用户是否有活动比如给优惠卷
                try {
                    //领取优惠卷，如果领取不成功不应该抛异常
                    //类型1 新用户注册卷
                    this.obtionCoupon(userDO,brandType,1,null);
                }catch (Exception e){
                    logger.info("领取优惠卷失败",e);
                }
            } else {
                userDO = userDOS.get(0);
                UserDO userUpdateDO = new UserDO();
                userUpdateDO.setId(userDO.getId());
                userUpdateDO.setGmtLastLogin(new Date());
                userUpdateDO.setLastLoginIp(ip);
                userMapper.updateById(userUpdateDO);
            }
            //检查帐号是否已经冻结
            if (userDO.getStatus() == 0) {
                throw new AppServiceException(ExceptionDefinition.USER_CAN_NOT_ACTICE);
            }
            String accessToken = GeneratorUtil.genSessionId();
            UserDTO userDTO = new UserDTO();
            BeanUtils.copyProperties(userDO, userDTO);
            userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
            userDTO.setAccessToken(accessToken);
            return userDTO;
        } else {
            logger.info("[微信登录] 回复失败 回复报文：" + body);
            throw new AppServiceException(ExceptionDefinition.USER_THIRD_UNEXPECT_RESPONSE);
        }

    }

    public void  obtionCoupon(UserDO userDO, Long brandType,Integer type,Long zzjgId) throws AppServiceException {
        /**
         * 1、获取总部下发的平台卷（默认总部发的优惠卷叫平台卷，该类型的卷所有店铺都可以使用
         * 2、把这些优惠卷注册到用户钱包中
         */
        if(brandType==null)brandType=2L;
        List<CouponDTO> obtainableCouponByPlatform = couponService.getObtainableCouponByPlatform (brandType, userDO.getId(),type,zzjgId);
        for(int i=0;i<obtainableCouponByPlatform.size();i++){
            CouponDTO couponDTO = obtainableCouponByPlatform.get(i);
            if(couponDTO.getSurplus()<1) continue;
            Long couponId = couponDTO.getId();
            couponService.obtainCoupon(couponId,userDO.getId());
        }


    }

    /**
     * 微信公众号浏览器H5登录
     * @param ip
     * @param raw
     * @return
     * @throws Exception
     */
    private UserDTO wechatH5Login(String ip, String raw) throws Exception {
        //H5 微信公众号网页登录
        String json = okHttpClient.newCall(
                new Request.Builder().url("https://api.weixin.qq.com/sns/oauth2/access_token?appid="
                        + this.unimallWxProperties.getH5AppId() + "&secret=" + this.unimallWxProperties.getH5AppSecret() + "&code=" + raw + "&grant_type=authorization_code").build()).execute().body().string();
        JSONObject jsonObject = JSONObject.parseObject(json);
        Integer errcode = jsonObject.getInteger("errcode");
        if (errcode == null || errcode == 0) {
            String openid = jsonObject.getString("openid");
            List<UserDO> userDOS = userMapper.selectList(new QueryWrapper<UserDO>().eq("open_id", openid).eq("login_type", UserLoginType.H5_WEIXIN.getCode()));
            if (!org.springframework.util.CollectionUtils.isEmpty(userDOS)) {
                //若用户已经注册，则直接返回用户
                String accessToken = GeneratorUtil.genSessionId();
                UserDTO userDTO = new UserDTO();
                BeanUtils.copyProperties(userDOS.get(0), userDTO);
                userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
                userDTO.setAccessToken(accessToken);
                return userDTO;
            } else {
                String userAccessToken = jsonObject.getString("access_token");
                //通过用户AccessToken换取用户信息
                String userInfoJson = okHttpClient.newCall(
                        new Request.Builder().url("https://api.weixin.qq.com/sns/userinfo?access_token="
                                + userAccessToken + "&openid=" + openid + "&lang=zh_CN").build()).execute().body().string();
                JSONObject userInfoJsonObject = JSONObject.parseObject(userInfoJson);
                Date now = new Date();
                UserDO newUserDO = new UserDO();
                newUserDO.setLoginType(UserLoginType.H5_WEIXIN.getCode());
                newUserDO.setNickname(userInfoJsonObject.getString("nickname"));
                newUserDO.setAvatarUrl(userInfoJsonObject.getString("headimgurl"));
                newUserDO.setGender(userInfoJsonObject.getInteger("sex"));
                newUserDO.setOpenId(openid);
                newUserDO.setLastLoginIp(ip);
                newUserDO.setGmtLastLogin(now);
                newUserDO.setGmtUpdate(now);
                newUserDO.setGmtCreate(now);
                userMapper.insert(newUserDO);
                //这一步是为了封装上数据库上配置的默认值
                UserDO userDO = userMapper.selectById(newUserDO.getId());
                String accessToken = GeneratorUtil.genSessionId();
                UserDTO userDTO = new UserDTO();
                BeanUtils.copyProperties(userDO, userDTO);
                userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
                userDTO.setAccessToken(accessToken);
                return userDTO;
            }
        } else {
            throw new AppServiceException(ExceptionDefinition.USER_THIRD_PART_LOGIN_FAILED);
        }
    }

    /**
     * 微信APP登录
     * @param ip
     * @param raw
     * @return
     * @throws Exception
     */
    private UserDTO wechatAppLogin(String ip, String raw) throws Exception {
        //UNI-APP 的 微信APP登录 APPSecret是保存在前端的。这点非常不安全。但是用了他的框架，也没有办法
        JSONObject jsonObject = JSONObject.parseObject(raw);
        JSONObject authResult = jsonObject.getJSONObject("authResult");
        String openid = authResult.getString("openid");
        List<UserDO> userDOS = userMapper.selectList(new QueryWrapper<UserDO>().eq("open_id", openid).eq("login_type", UserLoginType.APP_WEIXIN.getCode()));
        UserDO userDO;
        if (org.springframework.util.CollectionUtils.isEmpty(userDOS)) {
            //创建新用户
            Date now = new Date();
            UserDO newUserDO = new UserDO();
            newUserDO.setLoginType(UserLoginType.APP_WEIXIN.getCode());
            newUserDO.setOpenId(openid);
            newUserDO.setLastLoginIp(ip);
            newUserDO.setGmtLastLogin(now);
            newUserDO.setGmtUpdate(now);
            newUserDO.setGmtCreate(now);
            userMapper.insert(newUserDO);
            //这一步是为了封装上数据库上配置的默认值
            userDO = userMapper.selectById(newUserDO.getId());
        } else {
            userDO = userDOS.get(0);
            UserDO userUpdateDO = new UserDO();
            userUpdateDO.setId(userDO.getId());
            userUpdateDO.setGmtLastLogin(new Date());
            userUpdateDO.setLastLoginIp(ip);
            userMapper.updateById(userUpdateDO);
        }
        //检查帐号是否已经冻结
        if (userDO.getStatus() == 0) {
            throw new AppServiceException(ExceptionDefinition.USER_CAN_NOT_ACTICE);
        }
        String accessToken = GeneratorUtil.genSessionId();
        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(userDO, userDTO);
        userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
        userDTO.setAccessToken(accessToken);
        return userDTO;
    }

    /*************************第三方登录 END******************************/
}
